<template>
  <div>
    <TopBar title="扫雷"></TopBar>
    <text class="btn">雷数{{board}}&nbsp;&nbsp;&nbsp;&nbsp;生命{{shengming}}</text>
    <div class="wrapper">
      <div class="wrapper-box">
        <div v-for="r in row" :key="r.index" style="flex-direction: row; flex: 1;">
          <div v-for="c in r.col" :key="c.tid" style="flex: 1;">
            <text :tid="c.tid" v-on:click="onclick" v-on:longpress="onlongpress" v-bind:class="[c.state, 'tile']" :around="c.around">{{c.text}}</text>
          </div>
        </div>
      </div>
    </div>
    <text v-on:click="restart" class="btn">重玩</text>
  </div>
</template>

<script>
import TopBar from '@/components/TopBar'
const modal = weex.requireModule('modal')
export default {
  components: { TopBar },
  data () {
    return {
      size: 9,
      max: 20,
      maxShengMing: 1,
      board: 0,
      shengming: 0,
      row: [],
      vector: [[-1, 0], [-1, -1], [0, -1], [1, -1], [1, 0], [1, 1], [0, 1], [-1, 1]],
      strings: {
        mine: '',
        flag: '',
        win: '胜利!',
        lose: '你输了~'
      },
      finished: false
    }
  },
  methods: {
    map: function (x, y, callback) { // visit tiles around (x, y)
      for (var i = 0; i < 8; ++i) {
        var mx = x + this.vector[i][0]
        var my = y + this.vector[i][1]
        if (mx >= 0 && my >= 0 && mx < this.size && my < this.size) {
          callback(this.row[mx].col[my])
        }
      }
    },
    dfs: function (tile) { // dfs a tile
      var pos = this.position(tile.tid)
      var context = this
      tile.state = 'open'
      this.map(pos['x'], pos['y'], function (node) {
        if (node.around === 0 && node.state === 'normal') { // dfs
          context.dfs(node) // dfs recursively
        } else {
          context.display(node) // display tile
        }
      })
    },
    random: function (min, max) { // generate random number between [min, max)
      return parseInt(Math.random() * (max - min) + min)
    },
    plant: function () { // arrange mines
      var count = 0
      while (count < this.max) {
        var x = this.random(0, this.size)
        var y = this.random(0, this.size)
        var tile = this.row[x].col[y]
        if (tile.value === 0) {
          ++count
          tile.value = 1
        }
      }
    },
    calculate: function () { // calculate values around tiles
      for (var i = 0; i < this.size; ++i) {
        for (var j = 0; j < this.size; ++j) {
          var around = 0
          this.map(i, j, function (tile) {
            around += tile.value
          })
          this.row[i].col[j].around = around
        }
      }
    },
    restart: function (e) { // restart game
      var row = []
      // var count = 0s
      this.board = this.max // display remain mines
      this.shengming = this.maxShengMing
      this.finished = false
      for (var i = 0; i < this.size; ++i) { // init data-binding
        var col = { 'col': [] }
        for (var j = 0; j < this.size; ++j) {
          var tid = i * this.size + j
          col['col'][j] = {
            tid: '' + tid,
            state: 'normal',
            value: 0,
            text: '',
            around: 0
          }
        }
        row[i] = col
      }
      this.row = row // will cause view tree rendering
      this.plant() // arrange mines
      this.calculate() // calculate around values
      console.log(this.row)
    },
    unfinished: function () { // check game status
      var finished = this.finished
      if (this.finished) { // restart if finished
        this.restart()
      }
      return !finished
    },
    position: function (tid) { // return (x, y) with tile id
      var row = parseInt(tid / this.size)
      var col = tid % this.size
      return { x: row, y: col }
    },
    display: function (tile) {
      tile.state = 'open'
      tile.text = (tile.around === 0) ? '' : tile.around
    },
    tile: function (event) { // return tile object with click event
      let tid
      if (event.target.attr) {
        tid = event.target.attr['tid']
      } else {
        tid = event.target.attributes['tid'].nodeValue
      }
      var pos = this.position(tid)
      return this.row[pos['x']].col[pos['y']]
    },
    onclick: function (event) { // onclick tile
      if (this.unfinished()) {
        var tile = this.tile(event)
        if (tile.state === 'normal') {
          if (tile.value === 1) { // lose game
            this.onfail()
          } else { // open it
            this.display(tile)
            if (tile.around === 0) {
              this.dfs(tile) // start dfs a tile
            }
          }
        } else if (tile.state === 'open' && tile.value === 0) {
          let context = this
          let pos = this.position(tile.tid)
          let arrValue = []
          let arrNode = []
          this.map(pos['x'], pos['y'], function (node) {
            if (node.state === 'normal') {
              arrNode.push(node)
              arrValue.push(node.value)
            }
          })
          arrValue = Array.from(new Set(arrValue))
          if (arrValue.length === 1) {
            arrNode.forEach(function (node) {
              if (arrValue[0] === 1) {
                node.state = 'flag'
                --context.board
              } else if (node.around === 0 && node.state === 'normal') { // dfs
                context.dfs(node) // dfs recursively
              } else {
                context.display(node) // display tile
              }
            })
          }
        }
        this.judge() // game judgement
      }
    },
    onlongpress: function (event) { // onlongpress tile
      if (this.unfinished()) {
        var tile = this.tile(event)
        tile.state = tile.state === 'flag' ? 'normal' : 'flag'
        if (tile.state === 'flag') {
          --this.board
          tile.text = this.strings.flag // flag
        } else {
          ++this.board
          tile.text = ''
        }
        this.judge()
      }
    },
    foreach: function (callback) { // enumerate all tiles
      for (var i = 0; i < this.size; ++i) {
        for (var j = 0; j < this.size; ++j) {
          callback(this.row[i].col[j])
        }
      }
    },
    judge: function () {
      var count = 0
      this.foreach(function (tile) {
        if (tile.state === 'open' || tile.state === 'flag') {
          ++count
        }
      })
      if (count === this.size * this.size) { // win
        this.finished = true
        modal.alert({
          message: this.strings.win,
          okTitle: '确定'
        })
      }
    },
    onfail: function () { // fail
      modal.alert({
        message: this.strings.lose,
        okTitle: '确定'
      })
      this.finished = true
      var mine = this.strings.mine
      this.foreach(function (tile) {
        if (tile.value === 1) {
          tile.text = mine
        }
      })
    }
  },
  created: function () {
    this.restart()
  }
}
</script>

<style scoped>
.wrapper {
  width: 750px;
  height: 750px;
  overflow: scroll;
}

.btn {
  margin: 2px;
  background-color: #e74c3c;
  color: #ffffff;
  text-align: center;
  flex: 1;
  font-size: 66px;
  height: 80px;
}

.normal {
  background-color: #95a5a6;
}

.open {
  background-color: #34495e;
  color: #ffffff;
}

.flag {
  background-color: #ff0000;
}

.tile {
  margin: 2px;
  font-size: 66px;
  height: 80px;
  width: 80px;
  text-align: center;
}
</style>
